package com.alibaba.csp.sentinel.dashboard.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.TinyFlowRuleEntity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.google.common.base.Function;
import com.google.common.collect.Multimaps;

@Component
public class RefreshTask {
    @Autowired
    private AppManagement appManagement;
    
    @Autowired
    private SentinelApiClient sentinelApiClient;
    @Value("${zk.location}")
    private String zkAddress;
	
	public static volatile boolean running = false;
	
	private CuratorFramework zkClient;
	
	private static Logger LOGGER = LoggerFactory.getLogger(RefreshTask.class);
	@Scheduled(fixedRate = 30000)
	public void refreshInfo(){
		if(!running){
			try{
				running = true;
				//
				List<FlowRuleEntity> allFlows = new ArrayList<>(); 
				List<AppInfo> list = new ArrayList<>(appManagement.getBriefApps());
			    Collections.sort(list, Comparator.comparing(AppInfo::getApp));
			    //获取所有的 flowrules
				for(AppInfo ai: list){
					String appName = ai.getApp();
					List<MachineInfo> ms = new ArrayList<>(ai.getMachines());
					Collections.sort(ms);
					for(MachineInfo mi : ms){
						List<FlowRuleEntity> flows = sentinelApiClient.fetchFlowRuleOfMachine(appName, mi.getIp(), mi.getPort());
						if(flows != null){
							allFlows.addAll(flows);
						}
					}
				}
				
				//将flowrules去掉 ip, port保存到zk中
				List<TinyFlowRuleEntity> tinyFlows = new ArrayList<>(); 
				for(FlowRuleEntity fre : allFlows){
					TinyFlowRuleEntity tfre = new TinyFlowRuleEntity();
					BeanUtils.copyProperties(fre, tfre);
					if(!tinyFlows.contains(tfre)){		//防止重复
						tinyFlows.add(tfre);
					}
				}
				//将数据写到zk的节点中去
				//按appname分组数据
				Map<String, Collection<TinyFlowRuleEntity>> map = Multimaps.index(tinyFlows, new Function<TinyFlowRuleEntity, String>() {

					@Override
					public String apply(TinyFlowRuleEntity input) {
						return input.getApp();
					}
				}).asMap();
				
				
				SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
				filter.getExcludes().add("app");
				for(Map.Entry<String, Collection<TinyFlowRuleEntity>> entry: map.entrySet()){
					try {
						zkClient.create().orSetData().creatingParentsIfNeeded()
						.withMode(CreateMode.PERSISTENT).forPath("/sentinel_datasource_zk/" + entry.getKey(), 
								JSON.toJSONString(entry.getValue(), filter).getBytes());
					} catch (Exception e) {
						LOGGER.error(e.getMessage(),e);
					}
				}
				
			}finally{
				running = false;
			}
		}
		
	}
	
	@PostConstruct
	public void initZkClient(){
		RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
		CuratorFramework client = CuratorFrameworkFactory.newClient(zkAddress,retryPolicy);
		client.start();
		zkClient = client;
	}
}
